﻿package com.crazy.common.file;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.util.WebUtils;
/**
 * @author Zhang Weiwei
 * @since 2013-9-17下午9:59:18
 */
public abstract class MyCommonsFileUploadSupport {
	protected static class MultipartParsingResult {
		private final MultiValueMap<String, MultipartFile> multipartFiles;
		private final Map<String, String> multipartParameterContentTypes;
		private final Map<String, String[]> multipartParameters;
		public MultipartParsingResult(
				MultiValueMap<String, MultipartFile> mpFiles,
				Map<String, String[]> mpParams,
				Map<String, String> mpParamContentTypes) {
			this.multipartFiles = mpFiles;
			this.multipartParameters = mpParams;
			this.multipartParameterContentTypes = mpParamContentTypes;
		}
		public MultiValueMap<String, MultipartFile> getMultipartFiles() {
			return this.multipartFiles;
		}
		public Map<String, String> getMultipartParameterContentTypes() {
			return this.multipartParameterContentTypes;
		}
		public Map<String, String[]> getMultipartParameters() {
			return this.multipartParameters;
		}
	}
	private String[] fileExtes;
	private final DiskFileItemFactory fileItemFactory;
	private final FileUpload fileUpload;
	protected final Log logger = LogFactory.getLog(getClass());
	private boolean uploadTempDirSpecified = false;
	public MyCommonsFileUploadSupport() {
		this.fileItemFactory = newFileItemFactory();
		this.fileUpload = newFileUpload(getFileItemFactory());
	}
	protected void cleanupFileItems(
			MultiValueMap<String, MultipartFile> multipartFiles) {
		for (List<MultipartFile> files : multipartFiles.values()) {
			for (MultipartFile file : files) {
				if (file instanceof CommonsMultipartFile) {
					CommonsMultipartFile cmf = (CommonsMultipartFile) file;
					cmf.getFileItem().delete();
					if (logger.isDebugEnabled()) {
						String msg = "Cleaning up multipart file [%s] with original filename [%s], stored %s";
						Object[] args = {cmf.getName(),
								cmf.getOriginalFilename(),
								cmf.getStorageDescription()};
						logger.debug(String.format(msg, args));
					}
				}
			}
		}
	}
	private String determineEncoding(String contentTypeHeader,
			String defaultEncoding) {
		if (!StringUtils.hasText(contentTypeHeader)) {
			return defaultEncoding;
		}
		MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
		Charset charset = contentType.getCharSet();
		return (charset != null ? charset.name() : defaultEncoding);
	}
	protected String getDefaultEncoding() {
		String encoding = getFileUpload().getHeaderEncoding();
		if (encoding == null) {
			encoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
		}
		return encoding;
	}
	public String[] getFileExtes() {
		return fileExtes;
	}
	public DiskFileItemFactory getFileItemFactory() {
		return this.fileItemFactory;
	}
	public FileUpload getFileUpload() {
		return this.fileUpload;
	}
	public boolean isAllowed(String name) {
		name = name.toLowerCase();
		for (String fileExte : this.fileExtes) {
			if (name.endsWith(fileExte)) {
				return false;
			}
		}
		return true;
	}
	protected boolean isUploadTempDirSpecified() {
		return this.uploadTempDirSpecified;
	}
	protected DiskFileItemFactory newFileItemFactory() {
		return new DiskFileItemFactory();
	}
	protected abstract FileUpload newFileUpload(FileItemFactory fileItemFactory);
	protected MultipartParsingResult parseFileItems(List<FileItem> fileItems,
			String encoding) {
		MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
		Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
		Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();
		for (FileItem fileItem : fileItems) {
			if (fileItem.isFormField()) {
				String value;
				String partEncoding = determineEncoding(
						fileItem.getContentType(), encoding);
				if (partEncoding != null) {
					try {
						value = fileItem.getString(partEncoding);
					} catch (UnsupportedEncodingException ex) {
						if (logger.isWarnEnabled()) {
							String msg = "Could not decode multipart item '%s' with encoding '%s': using platform default";
							Object[] args = {fileItem.getFieldName(),
									partEncoding};
							logger.warn(String.format(msg, args));
						}
						value = fileItem.getString();
					}
				} else {
					value = fileItem.getString();
				}
				String[] curParam = multipartParameters.get(fileItem
						.getFieldName());
				if (curParam == null) {
					multipartParameters.put(fileItem.getFieldName(),
							new String[]{value});
				} else {
					String[] newParam = StringUtils.addStringToArray(curParam,
							value);
					multipartParameters.put(fileItem.getFieldName(), newParam);
				}
				multipartParameterContentTypes.put(fileItem.getFieldName(),
						fileItem.getContentType());
			} else {
				CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
				if (this.fileExtes != null && !file.isEmpty()) {
					if (this.isAllowed(file.getOriginalFilename())) {
						throw new RuntimeException(
								String.format("不允许上传[%s]此类格式的文件！",
										file.getOriginalFilename()));
					}
				}
				multipartFiles.add(file.getName(), file);
				if (logger.isDebugEnabled()) {
					String msg = "Found multipart file [%s] of size %s bytes with original filename [%s], stored %s";
					Object[] args = {file.getName(), file.getSize(),
							file.getOriginalFilename(),
							file.getStorageDescription()};
					logger.debug(String.format(msg, args));
				}
			}
		}
		return new MultipartParsingResult(multipartFiles, multipartParameters,
				multipartParameterContentTypes);
	}
	protected FileUpload prepareFileUpload(String encoding) {
		FileUpload fileUpload = getFileUpload();
		FileUpload actualFileUpload = fileUpload;
		if (encoding != null
				&& !encoding.equals(fileUpload.getHeaderEncoding())) {
			actualFileUpload = newFileUpload(getFileItemFactory());
			actualFileUpload.setSizeMax(fileUpload.getSizeMax());
			actualFileUpload.setHeaderEncoding(encoding);
		}
		return actualFileUpload;
	}
	public void setDefaultEncoding(String defaultEncoding) {
		this.fileUpload.setHeaderEncoding(defaultEncoding);
	}
	public void setFileExtes(String[] fileExtes) {
		this.fileExtes = fileExtes;
	}
	public void setMaxInMemorySize(int maxInMemorySize) {
		this.fileItemFactory.setSizeThreshold(maxInMemorySize);
	}
	public void setMaxUploadSize(long maxUploadSize) {
		this.fileUpload.setSizeMax(maxUploadSize);
	}
	public void setUploadTempDir(Resource uploadTempDir) throws IOException {
		if (!uploadTempDir.exists() && !uploadTempDir.getFile().mkdirs()) {
			String msg = "Given uploadTempDir [%s] could not be created";
			throw new IllegalArgumentException(String.format(msg,
					uploadTempDir.toString()));
		}
		this.fileItemFactory.setRepository(uploadTempDir.getFile());
		this.uploadTempDirSpecified = true;
	}
}